//进程的心跳代码学习
#include "_public.h"
using namespace idc;


//进程心跳信息的结构体
struct stprocinfo
{
    int pid=0;      //进程id
    char pname[51] = {0};   //进程名称
    int timeout = 0;        //超时时间，s
    time_t atime = 0;       //最后一次心跳

    stprocinfo(){};
    stprocinfo(const int in_pid,const string& in_pname,const int in_timeout,const time_t in_atime)
    :pid(in_pid),timeout(in_timeout),atime(in_atime)
    {
        strncpy(pname,in_pname.c_str(),50);
    }
};

int m_shmid=-1; //共享内存的id
stprocinfo *m_shm = nullptr;    //指向共享内存的地址空间
int m_pos = -1;    //用于存放当前进程在数组中的下标
void EXIT(int sig);

int main()
{
    //处理程序的退出信号
    signal(SIGINT,EXIT);signal(SIGTERM,EXIT);

    //创建/获取共享内存
    if((m_shmid = shmget((key_t)0x5095,1000*sizeof(struct stprocinfo),0666|IPC_CREAT))== -1)
    {
        printf("创建/获取共享内存(%x)失败\n",0x5095);
        return -1;
    }

    //将共享内存连接到当前进程的地址空间
    m_shm=(struct stprocinfo*)shmat(m_shmid,0,0);

    //测试
    // for(int i =0;i<1000;i++)
    // {
    //     if(m_shm[i].pid !=0)
    //     {
    //         printf("i=%d,pid=%d,pname=%s,timeout=%d,atime=%d\n",i,m_shm[i].pid,m_shm[i].pname,m_shm[i].timeout,m_shm[i].atime);
    //     }
    // }




    //把当前进程的信息填充到结构体中
    // stprocinfo procinfo;
    // memset(&procinfo,0,sizeof(struct stprocinfo));
    // procinfo.pid=getpid();                  //进程号
    // strncpy(procinfo.pname,"server1",50);   //进程名
    // procinfo.timeout = 30;                  //超时时间
    // procinfo.atime=time(0);                 //当前时间
    stprocinfo procinfo(getpid(),"server1",30,time(0));

    csemp semlock;      //用于给共享内存加锁的信号量

   if(semlock.init(0x5095) == false)
   {
    printf("创建/获取信号量(%x)失败\n",0x5095);
    return -1;
   }
   

    //如果一个进程异常退出，没清理自己的心跳信息
    //如果当前进程重用了pid，所以，如果共享内存中已存在当前进程编号，一定是其他进程残留的信息，
    //当前进程应该重新使用这个位置
    for(int i =0;i<1000;i++)
    {
        if(m_shm[i].pid == procinfo.pid)
        {
            m_pos=i;
            printf("找到旧位置i =%d",i);
            break;
        }
    }

    if(m_pos == -1)
    {
        //在共享内存中寻找一个空的位置，把当前进程的结构体保存到共享内存
        for(int i = 0;i<1000;i++)
        {
            if((m_shm+i)->pid == 0) //如果pid是空的，表明是空位置
            {
                m_pos=i;
                printf("找到新位置i=%d\n",i);
                break;
            }
        }
    }

    //如果-1，表明没找到空位置，说明共享内存已用完
    if(m_pos == -1)
    {
        semlock.post();//解锁
        printf("共享内存已用完\n");
        EXIT(-1);
    }
    
    //如果找到，把当前进程的结构体保存到共享内存
    memcpy(&m_shm[m_pos],&procinfo,sizeof(struct st_procinfo));
    semlock.post();//解锁
    while(1)
    {
        printf("服务程序运行中..\n");
    
        //更新进程心跳信息
        sleep(25);
        m_shm[m_pos].atime=time(0);
        sleep(25);
        m_shm[m_pos].atime=time(0);
    }



    return 0;
}

void EXIT(int sig)
{
    printf("sig=%d\n",sig);
    //从共享内存中删除当前进程的心跳信息
    if(m_pos!=-1) memset(m_shm+m_pos,0,sizeof(struct stprocinfo));

    //把共享内存从当前进程分离
    if(m_shm!=0) shmdt(m_shm);

    exit(0);
}